﻿using System;
using System.Reflection.Emit;
using Sigil.Impl;

namespace Sigil
{
	public partial class Emit<DelegateType>
	{
		/// <summary>
		/// Expects a destination pointer, a source pointer, and a length on the stack.  Pops all three values.
		///
		/// Copies length bytes from destination to the source.
		/// </summary>
		public Emit<DelegateType> CopyBlock(Boolean isVolatile = false, Int32? unaligned = null)
		{
			if (unaligned.HasValue && (unaligned != 1 && unaligned != 2 && unaligned != 4))
			{
				throw new ArgumentException("unaligned must be null, 1, 2, or 4", "unaligned");
			}

			if (!AllowsUnverifiableCIL)
			{
				FailUnverifiable("CopyBlock");
			}

			if (isVolatile)
			{
				UpdateState(OpCodes.Volatile, Wrap(StackTransition.None(), "CopyBlock"));
			}

			if (unaligned.HasValue)
			{
				UpdateState(OpCodes.Unaligned, (Byte)unaligned.Value, Wrap(StackTransition.None(), "CopyBlock"));
			}

			var transition =
					new[]
                {
                    new StackTransition(new[] { typeof(Int32), typeof(NativeIntType), typeof(NativeIntType) }, Type.EmptyTypes),
                    new StackTransition(new[] { typeof(Int32), typeof(Byte*), typeof(NativeIntType) }, Type.EmptyTypes),
                    new StackTransition(new[] { typeof(Int32), typeof(Byte).MakeByRefType(), typeof(NativeIntType) }, Type.EmptyTypes),
                    new StackTransition(new[] { typeof(Int32), typeof(NativeIntType), typeof(Byte*) }, Type.EmptyTypes),
                    new StackTransition(new[] { typeof(Int32), typeof(Byte*), typeof(Byte*) }, Type.EmptyTypes),
                    new StackTransition(new[] { typeof(Int32), typeof(Byte).MakeByRefType(), typeof(Byte*) }, Type.EmptyTypes),
                    new StackTransition(new[] { typeof(Int32), typeof(NativeIntType), typeof(Byte).MakeByRefType() }, Type.EmptyTypes),
                    new StackTransition(new[] { typeof(Int32), typeof(Byte*), typeof(Byte).MakeByRefType() }, Type.EmptyTypes),
                    new StackTransition(new[] { typeof(Int32), typeof(Byte).MakeByRefType(), typeof(Byte).MakeByRefType() }, Type.EmptyTypes)
                };

			UpdateState(OpCodes.Cpblk, Wrap(transition, "CopyBlock"));

			return this;
		}
	}
}